Skip to content

Add Rust matrix support for PauliProductRotation#15879

Closed
Adithyaphani wants to merge 9 commits intoQiskit:mainfrom
Adithyaphani:fix-15869-ppr-matrix-rust
Closed

Add Rust matrix support for PauliProductRotation#15879
Adithyaphani wants to merge 9 commits intoQiskit:mainfrom
Adithyaphani:fix-15869-ppr-matrix-rust

Conversation

@Adithyaphani
Copy link
Copy Markdown

@Adithyaphani Adithyaphani commented Mar 25, 2026

  • I have added the tests to cover my changes.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.

Fixes #15869

### Summary

Adds Rust-side matrix support for PauliProductRotation, which was previously available only in the Python layer.

This ensures consistent behavior across Python and Rust implementations and enables correct matrix evaluation for Pauli product rotations in the Rust backend.

### Details and comments

This PR introduces the missing matrix functionality for PauliProductRotation in the Rust circuit layer:

  • Implemented pauli_product_rotation_matrix(theta, pauli_str) in
    crates/circuit/src/gate_matrix.rs, computing:
    exp(-i * θ / 2 * P) = cos(θ / 2) * I - i * sin(θ / 2) * P
    using tensor products of single-qubit Pauli operators.

  • Implemented Operation::matrix() for PauliProductRotation in
    crates/circuit/src/operations.rs.

  • Verified that PackedInstruction::try_matrix() correctly delegates
    to the operation matrix implementation in
    crates/circuit/src/packed_instruction.rs.

  • Added Python test coverage in
    test/python/circuit/test_pauli_product_rotation_matrix.py, including:

    • Unitarity validation
    • Known-angle special cases
    • Statevector consistency checks

### Testing

cargo test -p qiskit-circuit
python -m pytest test/python/circuit/test_pauli_product_rotation_matrix.py -v

@Adithyaphani Adithyaphani requested a review from a team as a code owner March 25, 2026 19:34
@qiskit-bot qiskit-bot added the Community PR PRs from contributors that are not 'members' of the Qiskit repo label Mar 25, 2026
@qiskit-bot
Copy link
Copy Markdown
Collaborator

Thank you for opening a new pull request.

Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient.

While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone.

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 25, 2026

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown
Collaborator

@Cryoris Cryoris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for opening a PR on this, @Adithyaphani. I think we can approach this differently to avoid re-implementing the logic to get the matrix for P, which already exists in the around the SparsePauliOp's matrix logic, see e.g.

pub fn to_matrix_dense<'py>(

We should separate the Python and Rust logic here so we can call the matrix construction without requiring the py: Python<'_> token. If you want to outline your approach before implementing it, that could be helpful.

In general: please avoid unnecessary changes in the files or adding unused functions -- it's perfectly fine to use AI but the contribution needs to be up to standard.

@jakelishman
Copy link
Copy Markdown
Member

This is a pre-written response.

I believe this may have been LLM generated without attribution.

Please confirm the model used, and that the PR was tested and checked by a human before submission, including validating that the PR truly solves the issue at it was described, and takes into account any comments from maintainers in the base issues.

@Adithyaphani
Copy link
Copy Markdown
Author

I understand the approach now @Cryoris — instead of re-implementing the Pauli
tensor product matrix logic in gate_matrix.rs, I should reuse the
existing matrix construction logic from SparsePauliOp in
crates/quantum_info/src/sparse_pauli_op.rs.

My planned approach:

  1. Extract a pure Rust function from the SparsePauliOp matrix logic
    (around line 935) that does not require a py: Python<'_> token —
    something like a standalone pauli_zx_to_matrix(z: &[bool], x: &[bool]) -> Array2<Complex64> that can be called from anywhere in Rust without
    holding the GIL.

  2. Call that function inside PauliProductRotation::matrix() in
    operations.rs to compute exp(-i * angle/2 * P) = cos(angle/2)*I

    • i*sin(angle/2)*P, where P comes from the extracted function.
  3. Remove the re-implemented pauli_product_rotation_matrix() function
    I added in gate_matrix.rs and any other unnecessary changes.

Does this align with what you had in mind? Happy to outline further
before implementing if you'd like to confirm the approach first.

@Cryoris
Copy link
Copy Markdown
Collaborator

Cryoris commented Mar 26, 2026

Yeah that sounds right 👍🏻

Also: if you used an LLM to generate the contribution, please attribute this and the model used in the issue description, as Jake pointed out above.

@Adithyaphani
Copy link
Copy Markdown
Author

@Cryoris I’ve updated the implementation to reuse the existing matrix construction logic and removed the redundant helper. I have also resolved the merge conflicts with the latest upstream changes.

As Jake pointed out regarding the LLM part .For transparency, I used ChatGPT (GPT-5.3) to help refine the approach and better understand the structure. The final implementation, however, was carefully reviewed and adapted manually to ensure alignment with the existing Qiskit codebase.

Please let me know if any further changes or improvements are required—I’d be happy to refine it or make modifications further.

@Adithyaphani
Copy link
Copy Markdown
Author

Hey @Cryoris , the earlier workflow failures were due to the previous implementation and dependency issues.

I’ve updated the code to use a local helper for Pauli matrix construction, resolved the merge conflicts, and addressed those issues. The latest commit now passes all checks.

Please let me know if any further refinements are needed.

@alexanderivrii
Copy link
Copy Markdown
Member

Thanks @Adithyaphani and @Cryoris. A few quick questions:

@Adithyaphani
Copy link
Copy Markdown
Author

Adithyaphani commented Mar 28, 2026

Hello @Cryoris and @jakelishman , I’ve now reworked the implementation to align with the suggested approach -> Fixed lint issues and ensured formatting compliance .Please let me know if any further refinements and modifications are needed.

@Adithyaphani
Copy link
Copy Markdown
Author

Hi @alexanderivrii .

Regarding the Kronecker product — yes, using ndarray::linalg::kron seems like a good and clean option. It already provides the required functionality, so it helps avoid reimplementing the tensor-product logic and keeps the code simpler and more maintainable.

On the matrix size — I agree this is an important consideration. Since the matrix grows exponentially with the number of qubits, constructing dense matrices can quickly become expensive in terms of both memory and computation. My current approach is to stay consistent with how matrix construction is handled elsewhere in Qiskit: only build the dense matrix when it is explicitly required, rather than adding additional layers that might increase overhead.

Overall, the goal is to keep the implementation lightweight by reusing the existing Rust-side logic and avoiding unnecessary duplication. Happy to adjust this further if there’s a preferred pattern to follow here.

Copy link
Copy Markdown
Member

@ShellyGarion ShellyGarion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution to qiskit. In addition to the previous comments, can you also add release notes?

Comment thread test/python/circuit/test_pauli_product_rotation_matrix.py Outdated
Comment thread test/python/circuit/test_pauli_product_rotation_matrix.py Outdated
@Adithyaphani
Copy link
Copy Markdown
Author

@ShellyGarion just to clarify, I had pushed an earlier commit before seeing the review comments. I’ve now addressed the feedback in a follow-up commit — including moving the tests to the existing file, aligning with the standard test structure, and updating the parametrization style. The latest state of the PR reflects all the requested changes, and I’m happy to make any further refinements if needed.

Comment thread crates/circuit/src/gate_matrix.rs Outdated
}

/// Kronecker (tensor) product of two complex matrices.
fn kron(a: &Array2<Complex64>, b: &Array2<Complex64>) -> Array2<Complex64> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, did you relate to Alexander's comment on using https://docs.rs/ndarray/latest/ndarray/linalg/fn.kron.html ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in addition, note that you should use cargo fmt

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. I’ve updated the implementation to avoid the custom kron helper and aligned it with the existing ndarray Kronecker-product path as suggested.

Noted — I’ve also run cargo fmt on the updated Rust code in the follow-up commit.

Copy link
Copy Markdown
Member

@ShellyGarion ShellyGarion Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, in addition please run cargo check and cargo clippy as there are some rust errors

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve re-run cargo check and cargo clippy with all targets and features, and addressed the reported issues. The code now builds cleanly without warnings.

Please let me know if you see anything else to improve.

@Adithyaphani
Copy link
Copy Markdown
Author

Adithyaphani commented Mar 29, 2026

Thanks for the note @ShellyGarion . I’ve updated the implementation to avoid the custom kron helper and aligned it with the suggested ndarray Kronecker-product path. I also ran cargo fmt on the updated Rust code in the follow-up commit. Yet any further modifications I'm happy to work on it.

Comment thread crates/circuit/src/operations.rs Outdated
Param::Float(f) => f,
_ => return None,
};
let pauli_mat = gate_matrix::pauli_zx_to_dense_matrix(&self.z, &self.x);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are calling gate_matrix::pauli_zx_to_dense_matrix, but the function you added in sparse_pauli_op.rs is named pauli_zx_to_matrix

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks for pointing that out.

I’ve updated the call to use pauli_zx_to_matrix and ensured it is correctly defined and used within gate_matrix to avoid cross-crate dependency issues. The naming and usage are now aligned.

Please let me know if anything else needs refinement.

Comment thread crates/circuit/src/operations.rs Outdated
Comment on lines +3580 to +3584
let identity = Array2::<Complex64>::eye(dim);
Some(
identity.mapv(|v| Complex64::new(v.re * cos_val, 0.))
+ pauli_mat.mapv(|v| Complex64::new(0., -sin_val) * v),
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the current identity.mapv(...) tells Rust to:

Iterate over every single one of the 256 elements (even the 240 zeros as it's identity).
Multiply it by cos_val.
Allocate space and write a new matrix with the results.

Instead we can think of doing

// Formula: cos(theta/2)I - i sin(theta/2)P
let mut result = pauli_mat * Complex64::new(0.0, -sin_val);
for i in 0..dim {
    result[[i, i]] += cos_val;
}
Some(result)

This may avoid unnecessary memory copies.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion — that makes sense.

I’ve updated the implementation to construct the result directly from the Pauli matrix and add the cosine term to the diagonal in-place, avoiding the extra allocation from identity.mapv(...). This should reduce unnecessary memory overhead.

Please let me know if this looks good now.

@Adithyaphani
Copy link
Copy Markdown
Author

Thanks for the review @ShellyGarion with a lot of patience you kept guiding me I resolved the merge conflict in operations.rs, aligned the PauliProductRotation matrix path, kept the helper in gate_matrix, updated the matrix construction to add the cosine term directly to the diagonal, and addressed the Rust/clippy issues raised during CI. I re-ran cargo fmt, cargo check, cargo clippy, and targeted circuit tests locally before pushing the update. Any further modifications I'm ready to work on it.

@ShellyGarion
Copy link
Copy Markdown
Member

ShellyGarion commented Mar 31, 2026

I'm sorry but this does not seem a high-quality enough PR for Qiskit despite of all of the guidelines of many of the qiskit core team members. I don't know if this is due to the use of LLM tools, but this makes it very difficult for us to continue reviewing this PR.

@Adithyaphani
Copy link
Copy Markdown
Author

Adithyaphani commented Mar 31, 2026

@ShellyGarion and @jakelishman , I'm extremely sorry for the mistakes I committed by making modifications in unnecessary files of repository, I goofed up near code parts of multiple files and I immensely appreciate your guidance & patience towards me in helping me to point out mistakes and go ahead in resolving this issue. I totally understand after so many CI checks failures and commits closing this issue is right . But I again raised a new PR which tries to solve the same issue without repeating the same mistakes. I hope you consider this pr .PR :#15927

I request you'll to go through this pr once and I hope this time it passes all CI checks successfully. Looking forward to your responses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community PR PRs from contributors that are not 'members' of the Qiskit repo

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Add PauliProductRotation matrix handling in Rust

8 participants